package ar.unc.famaf.pln.giveselector.classifier;

import give.formula.Atom;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import ar.unc.famaf.pln.giveselector.builders.ACLBuilder;
import ar.unc.famaf.pln.giveselector.context.Utterance;
import ar.unc.famaf.pln.giveselector.loganalyser.LogAnalyser;

public class ContinuousClassifier extends ACLClassifier 
{

	/**
	 * Runs the ContinuousClassificator over a file with annotated utterances
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException
	{
		String redirectOutput = null;
		String annotationFile = null;

		for (int i=0; i<args.length; i++)
		{
			String arg = args[i];
			if (arg.contentEquals("-r") || arg.contentEquals("--redirect"))
					redirectOutput = (i>=args.length-1) ? null : args[i+1];
			else if (arg.contentEquals("-a") || arg.contentEquals("--annotation"))
				annotationFile = (i>=args.length-1) ? null : args[i+1];
		}

		// Redirecting standard output to a file
		if (redirectOutput != null) 
		{
			System.setOut(
				new PrintStream(
					new BufferedOutputStream(
						new FileOutputStream(redirectOutput))));
		}

		if (annotationFile != null)
		{
			Map<String, Set<Utterance>> utterances = LogAnalyser.load(new File(annotationFile));
			ACLClassifier classificator = new ContinuousClassifier();
			Iterator<String> keys = utterances.keySet().iterator();
			while (keys.hasNext())
			{
				String worldName = keys.next();
				Set<Utterance> utts = classificator.classifyAll(utterances.get(worldName));
				utterances.put(worldName, utts);
			}
			classificator.printClassificationInfo(utterances);
			//classificator.printClassificationNumbers(utterances);
			ACLBuilder.save(new File("datas/action-utterances-with-id-classified-performance.bin.gz"), utterances);
		}
		else
		{
			System.out.println("You must specify at least the annotation file location: ");
			System.out.println("-a <annotation_file_location>");
			System.out.println("-annotation <annotation_file_location>");
		}

	}


	@Override
	public void printUtteranceInfo(UtteranceP u, float score)
	{
		System.out.println(score + " : [" + u.getId() + "]: " + u.getText());
	}


	@Override
	public void printClassificationInfo(Map<String, Set<Utterance>> utterances)
	{
		Iterator<String> worlds = utterances.keySet().iterator();
		int globalSize = 0;
		int nGlobalNotUsed = 0;
		int nGlobalS = 0;
		int nGlobalOccs = 0;
		while (worlds.hasNext())
		{
			String worldName = worlds.next();
			System.out.println("\n\n==== Classification Info World: " + worldName + " ====");
			Iterator<Utterance> utts = utterances.get(worldName).iterator();
			int nS = 0;
			int nOccs = 0;
			int nNotUsed = 0;

			while (utts.hasNext())
			{
				Utterance u = utts.next();
				System.out.println("Utterance: [" + u.getId() + "] " + u.getText());
				if (u instanceof UtteranceP)
				{
					System.out.println("Total Score: " + getScore(u));
					System.out.println("Occurrences:");
					List<Occurrence> occs = ((UtteranceP) u).getOccurrences(); 
					nOccs += occs.size();
					for (Occurrence o : occs)
					{
						if (o instanceof ClassifiedOccurrence)
						{
							float score = ((ClassifiedOccurrence) o).getScore();
							nS += (score > 0) ? 1 : 0;
							System.out.println("\t\tScore: " + score);
							for (Occurrence rel : ((ClassifiedOccurrence) o).getRelatedOccurrences())
							{
								if (rel instanceof ClassifiedOccurrence)
									System.out.println("\t\t\t\tScore: " + ((ClassifiedOccurrence) rel).getScore()
											+ " ts: " + rel.getTimestamp()
											+ " [" + rel.getOwner().getId() + "] " + rel.getOwner().getText());
							}
						}
					}
					System.out.println("");
				}
				else
				{
					System.out.println("Score: never used\n");
					nNotUsed++;
				}
			}
			
			System.out.println("==== Statistics for world: " + worldName + " ====");
			int size = utterances.get(worldName).size();
			System.out.println("Success Rate: " + nS + "/" + nOccs + " (" + LogAnalyser.percentage(nS, nOccs) + "%)");
			System.out.println("Failure Rate: " + (nOccs-nS) + "/" + nOccs + " (" + LogAnalyser.percentage(nOccs-nS, nOccs) + "%)");
			System.out.println("Utterances with P info: " + (size - nNotUsed) + " (" + LogAnalyser.percentage(size-nNotUsed, size)+"%)");
			System.out.println("Utterances with NO P info: " + nNotUsed + " (" + LogAnalyser.percentage(nNotUsed, size)+"%)");
			System.out.println("Utterances: " + size + " (100%)");
			globalSize += size;
			nGlobalNotUsed += nNotUsed;
			nGlobalS += nS;
			nGlobalOccs += nOccs;
		}
		System.out.println("\n\n==== Statistics for all worlds ====");
		System.out.println("Success Rate: " + nGlobalS + "/" + nGlobalOccs + " (" + LogAnalyser.percentage(nGlobalS, nGlobalOccs) + "%)");
		System.out.println("Failure Rate: " + (nGlobalOccs-nGlobalS) + "/" + nGlobalOccs + " (" 
				+ LogAnalyser.percentage(nGlobalOccs-nGlobalS, nGlobalOccs) + "%)");
		System.out.println("Utterances with P info: " + (globalSize - nGlobalNotUsed) + " (" 
					+ LogAnalyser.percentage(globalSize-nGlobalNotUsed, globalSize)+"%)");
		System.out.println("Utterances with NO P info: " + nGlobalNotUsed + " (" + LogAnalyser.percentage(nGlobalNotUsed, globalSize)+"%)");
		System.out.println("Utterances: " + globalSize + " (100%)");
	}


	@Override
	public ClassifiedOccurrence classify(Occurrence occ)
	{
		ClassifiedOccurrence co = null;

		List<Atom> expectedReaction = occ.getOwner().getResponse().getDiscretizedActions();

		occ.sortRelatedOccurrences();
		List<Occurrence> related = occ.getRelatedOccurrences();
		Occurrence last = null;
		if (related.isEmpty()){
			System.out.println("Empty!!!" + occ);
			return null;
		}

		last = related.get(related.size()-1);
		List<Atom> executedActions = last.getResponse().getDiscretizedActions();

		if (executedActions != null && !executedActions.isEmpty())
		{
			Atom firstExecuted = executedActions.get(0);
			Atom firstExpected = expectedReaction.get(0);
			float factor = 0f;

			if (firstExecuted.equals(firstExpected))
				factor = 1f;	// success
			else
				factor = -1f; 	// failure

			// the candidate will be the first Occurrence which is
			// 6 seconds before the last Occurrence.
			int candidate = -1;
			long t2 = last.getTimestamp().getTime();
			long t1 = t2;
			for (candidate=related.size()-1; candidate>=0 && t2-t1<6000; candidate--)
			{
				Occurrence o = related.get(candidate);
				t1 = o.getTimestamp().getTime();
			}
			candidate++;

			List<Float> scores = new ArrayList<Float>();
			float acum = 0f;
			int index = 0;
			for (int i=0; i<related.size(); i++)
			{
				scores.add(1f - (Math.abs((float) i - (float) candidate) / related.size()));
				acum += scores.get(i);
				if (related.get(i).equals(occ))
					index = i;
			}
			
			// Debugging purposes
			/*System.out.println("");
			for (int i=0; i<related.size(); i++)
			{
				System.out.println("Score: " + (scores.get(i) / acum * factor) + " [" + related.get(i).getOwner().getId() + "] "
					+ related.get(i).getOwner().getText());
			}
			System.out.println("");*/
			float score = (scores.get(index) / acum) * factor;

			co = new ClassifiedOccurrence(occ, score);
		}

		return co;
	}


	@Override
	public float getScore(Utterance utt)
	{
		float result = 0f;
		if (utt instanceof UtteranceP)
		{
			List<Occurrence> occs = ((UtteranceP) utt).getOccurrences(); 
			for (Occurrence o : occs)
			{
				if (o instanceof ClassifiedOccurrence)
					result += ((ClassifiedOccurrence) o).getScore();
			}
			result = result / ((float)occs.size());
		}
		return result;
	}

}
 